/*
 * FileProtocolServer.cpp
 *
 *  Created on: 26/06/2014
 *      Author: Caina
 */

#include "FileProtocolServer.h"

FileProtocolServer::FileProtocolServer() :
		socket(TCPSocket(nullptr, 7777)) {
	socket.open();
}

FileProtocolServer::~FileProtocolServer() {
	socket.close();
}

std::string FileProtocolServer::convert_ip_to_string(unsigned int ip) {
	unsigned int bit24 = 0xFF000000;
	unsigned int bit16 = 0x00FF0000;
	unsigned int bit8 = 0x0000FF00;
	unsigned int bit0 = 0x000000FF;
	char ipAddr[20];
	sprintf(ipAddr, "%u.%u.%u.%u", (ip & bit0), (ip & bit8) >> 8,
			(ip & bit16) >> 16, (ip & bit24) >> 24);
	return std::string(ipAddr);
}

void FileProtocolServer::handle_file_table(ByteQueue& queue) {
	Globals::file_table.lock();
	delete (Globals::file_table.get());
	Globals::file_table.get() = FileTable::deserialize(queue);
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_new_user(ByteQueue& queue) {
	auto user = User::deserialize(queue);
	Globals::peers.lock();
	Globals::user_table.get().push_back(user);
	Globals::peers.unlock();
}

void FileProtocolServer::handle_incoming_file(ByteQueue& queue) {
	auto file_name = queue.pop<std::string>();
	auto file = File(file_name, "wb");
	file.binary_write(queue.get_raw());
}

void FileProtocolServer::handle_send_file_to(ByteQueue queue) {
	auto file_name = queue.pop<std::string>();
	auto dstPeer = queue.pop<unsigned int>();

	auto file = File(file_name, "rb");

	ByteQueue sendQueue;
	sendQueue.push<std::string>(file_name);
	sendQueue.push(file.read_file_contents());
	sendQueue.push_front<unsigned int>(sendQueue.size());
	sendQueue.push_front<char>(MessageTypes::INCOMING_FILE);

	auto messageStream = sendQueue.get_raw();

	auto output_socket = TCPSocket(convert_ip_to_string(dstPeer).c_str(), 7777);
	output_socket.open();
	output_socket.send(messageStream);
	output_socket.close();
}

void FileProtocolServer::handle_delete_file(ByteQueue& queue) {
	auto file_name = queue.pop<std::string>();

	Globals::file_table.lock();
	Globals::file_table.get()->remove_file(file_name);
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_rename_file(ByteQueue& queue) {
	auto file_name_old = queue.pop<std::string>();
	auto file_name_new = queue.pop<std::string>();

	Globals::file_table.lock();
	printf("rename %s - %s\n", file_name_old.c_str(), file_name_new.c_str());
	Globals::file_table.get()->rename_file(file_name_old, file_name_new);
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_relocate_file_to(ByteQueue& queue) {
	handle_send_file_to(queue);
	auto file_name = queue.pop<std::string>();

	printf("Deletar arquivo %s\n", file_name.c_str());
	remove(file_name.c_str());
}

void FileProtocolServer::handle_create_directory(ByteQueue& queue) {
	auto dir_name_new = queue.pop<std::string>();
	Globals::file_table.lock();
	Globals::file_table.get()->make_directory(dir_name_new);
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_rename_directory(ByteQueue& queue) {
	auto dir_name_old = queue.pop<std::string>();
	auto dir_name_new = queue.pop<std::string>();

	Globals::file_table.lock();
	Globals::file_table.get()->rename_directory(dir_name_old, dir_name_new);
	Globals::file_table.get()->print_all();
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_delete_directory(ByteQueue& queue) {
	auto dir_name_old = queue.pop<std::string>();
	Globals::file_table.lock();
	Globals::file_table.get()->remove_directory(dir_name_old);
	Globals::file_table.unlock();
}

void FileProtocolServer::handle_peers(ByteQueue& queue) {
	Globals::peers.lock();
	Globals::peers.get() = Globals::deserialize_peers(queue);
	printf("Tabela de peers atualizada! Agora conheco %d peers!\n", (int) Globals::peers.get().size());
	Globals::peers.unlock();
}

void FileProtocolServer::handle_user_table(ByteQueue& queue) {
	Globals::user_table.lock();
	Globals::user_table.get() = Globals::deserialize_user_table(queue);
	Globals::user_table.unlock();
}

void FileProtocolServer::handle_message(TCPSocket client_socket) {
	char messageType;
	std::vector<char> messageStream;
	int messageLength;

	if (client_socket.recv<char>(messageType)) {
		client_socket.recv<int>(messageLength);
		client_socket.recv(messageLength, messageStream);
		auto queue = ByteQueue(messageStream);

		switch (messageType) {
		case MessageTypes::FILE_TABLE:
			handle_file_table(queue);
			break;

		case MessageTypes::NEW_USER:
			handle_new_user(queue);
			break;

		case MessageTypes::INCOMING_FILE:
			handle_incoming_file(queue);
			break;

		case MessageTypes::SEND_FILE_TO:
			handle_send_file_to(queue);
			break;

		case MessageTypes::DELETE_FILE:
			handle_delete_file(queue);
			break;

		case MessageTypes::RENAME_FILE:
			handle_rename_file(queue);
			break;

		case MessageTypes::RELOCATE_FILE_TO:
			handle_relocate_file_to(queue);
			break;

		case MessageTypes::CREATE_DIRECTORY:
			handle_create_directory(queue);
			break;

		case MessageTypes::RENAME_DIRECTORY:
			handle_rename_directory(queue);
			break;

		case MessageTypes::DELETE_DIRECTORY:
			handle_delete_directory(queue);
			break;

		case MessageTypes::PEERS:
			handle_peers(queue);
			break;

		case MessageTypes::USER_TABLE:
			handle_user_table(queue);
			break;

		default:
			printf("Chegou Desconhecido\n");
			break;
		}
	}
}

void FileProtocolServer::run() {
	while (Globals::active.get()) {
		TCPSocket client_socket_descriptor;
		if (socket.accept(client_socket_descriptor)) {
			Thread message_handler([client_socket_descriptor]() {
				handle_message(client_socket_descriptor);
			});
			message_handler.start();
		}
		SDL_Delay(20);
	}
}

